/*
 * MIT License
 *
 * Copyright (c) 2023-present, tangli
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package tony.captcha

import com.pig4cloud.captcha.ArithmeticCaptcha
import com.pig4cloud.captcha.ChineseCaptcha
import com.pig4cloud.captcha.GifCaptcha
import com.pig4cloud.captcha.SpecCaptcha
import java.time.Duration
import java.util.function.Supplier
import tony.captcha.autoconfigure.CaptchaProperties
import tony.captcha.autoconfigure.CaptchaType
import tony.core.SpringContexts
import tony.core.utils.throwIfFalse
import tony.redis.RedisManager
import tony.redis.RedisValues

/**
 * 验证码服务 单例类.
 *
 * @author tangli
 * @date 2023/05/25 19:38
 */
public data object CaptchaManager {
    private val captchaService: CaptchaService by SpringContexts.getBeanByLazy()

    private val captchaProperties: CaptchaProperties by SpringContexts.getBeanByLazy()

    /**
     * 生成验证码
     * @param [captchaId] 验证码ID
     * @param [type] 验证码类型, 可用来归类
     * @param [timeout] 过期时间
     * @param [captchaType] 验证码图片类型
     * @param [width] 宽度
     * @param [height] 高度
     * @param [len] 长度
     * @return [Captcha]
     * @author tangli
     * @date 2025/11/19 13:43
     */
    @JvmOverloads
    @JvmStatic
    public fun genCaptcha(
        captchaId: String,
        type: String,
        timeout: Duration = Duration.ofMinutes(1),
        captchaType: CaptchaType = captchaProperties.type,
        width: Int = captchaProperties.width,
        height: Int = captchaProperties.height,
        len: Int = captchaProperties.len,
        fontType: Int = 0,
    ): Captcha {
        val sourceCaptcha =
            when (captchaType) {
                CaptchaType.PNG -> SpecCaptcha(width, height, len)
                CaptchaType.GIF -> GifCaptcha(width, height, len)
                CaptchaType.CHINESE -> ChineseCaptcha(width, height, len)
                CaptchaType.ARITHMETIC -> ArithmeticCaptcha(width, height, len)
            }
        sourceCaptcha.setFont(fontType)
        val captcha = Captcha(captchaId, sourceCaptcha.toBase64(), type)
        val captchaKey = "${captchaProperties.prefix}:$type:$captchaId"
        val text: String = sourceCaptcha.text()
        RedisValues.set(captchaKey, text, timeout)
        return captcha
    }

    /**
     * 生成验证码缓存键
     * @param [captchaId] 验证码 ID
     * @param [type] 类型
     * @return [String]
     * @author tangli
     * @date 2025/09/26 10:17
     */
    public fun genCaptchaKey(
        captchaId: String,
        type: String,
    ): String =
        "${captchaProperties.prefix}:$type:$captchaId"

    /**
     * 验证码校验.
     *
     * 验证码校验不通过会直接抛错.
     * 成功会返回 回调 [verifySuccessFunc] 结果.
     * @param [captcha] 验证码对象
     * @param [message] 错误信息
     * @param [verifySuccessFunc] 回调
     * @return [R] [verifySuccessFunc] 回调的返回值
     * @author tangli
     * @date 2023/09/28 19:57
     */
    @JvmStatic
    @JvmOverloads
    public fun <R> verify(
        captcha: Captcha,
        message: String = "验证码错误",
        verifySuccessFunc: Supplier<R>,
    ): R {
        captchaService.verify(captcha).throwIfFalse(message)
        val result = verifySuccessFunc.get()
        RedisManager.delete(genCaptchaKey(captcha.captchaId, captcha.captchaType))
        return result
    }
}
